<?php
/**
 * UnCaptcha
 * @filesource
 */
/**
 * Namespace
 */
namespace framework\helper;
/**
 * Dependencies
 */
use framework\Front;
use framework\library\Language;
/**
 * UnCaptcha Class
 * 
 * A unique approach to the password entry problem.
 * 
 * The class constructs an easy to read image.  The image contains instructions
 * for selecting a specific (random) character from the text in the image and
 * further for placing that character either at the beginning or end of their 
 * password.
 * 
 * The image could presumably be OCR'd very easily.  The benefit is that the
 * human must interpret the psuedo-random instructions.
 * 
 * @todo Finish up this class
 * @todo Finish Documentation
 * 
 * @author Randy Casburn <randy@plurious.com>
 * @copyright Copyright Plurious Software, all rights reserved
 * @license MIT
 * @license http://www.opensource.org/licenses/mit-license.html
 * @link http://plurious.com/framework/documentation/classes/Front.html
 * @version 2.1
 */
class UnCaptcha {

    public $img;
    public $rules;
    public $location;
    public $char;
    private $config;
    private $language;
    private $input_length;
    private $img_text;
    private $img_width;
    private $img_height;
    private $margin = 9;
    private $img_font_size;
    
    /**
     * Build a unique character replacement image template with instructions.
     * 
     * @param string A string used for the process of modification
     * @param int The length of the input string so random insertion
     * @param int The width of the image created by this class
     * @param int The height of the image created by this class
     * @param int The GD default sizes must be 1-5
     */
    function __construct($string = null, $input_length = 0, $width = 0, $height = 0, $font_size = 5) {
        $registry = Front::getInstance();
        $this->config = $registry->get('config');
        $this->language = new Language();
        $this->language->getLanguage('unCaptcha');
        if (!$string) {
            $this->img_text = str_replace('  ', '', trim($this->config->get('auth_enhance_text')));
        } else {
            $this->img_text = trim($string);
        }
        if (!$input_length) {
            $this->input_length = $this->config->get('auth_min_password_length');
        } else {
            $this->input_length = $input_length;
        }
        if (!$width) {
            $this->img_width = $this->config->get('auth_captcha_width');
        } else {
            $this->img_width = $width;
        }
        if (!$height) {
            $this->img_height = $this->config->get('auth_captcha_height');
            
        } else {
            $this->img_height = $height;
        }
        $this->img_font_size = $font_size;

        $this->randomize();
        $this->image = $this->create_image();
    }

    protected function create_image() {
        $img_path = ROOT_DIR . 'base/images/captcha/';
        $img_url = ROOT_URL . 'base/images/captcha/';
        $img_font = ROOT_DIR . 'base/images/captcha/fonts/PontanoSans-Regular.ttf';

        if (!extension_loaded('gd')) {
            return FALSE;
        }
        $ver_info = gd_info();
        preg_match('/\d/', $ver_info['GD Version'], $match);
        $gd_ver = $match[0];
        if ($gd_ver < 2) {
            // Font size is in pixels
            $img_font_size = '14'; // yes - this is hard coded - yet over it ;-)
        } else {
            // Font size is in points
            $img_font_size = '16'; // yes - this is hard coded - yet over it ;-)
        }

        if (!@is_dir($img_path)) {
            return FALSE;
        }

        if (!is_writable($img_path)) {
            return FALSE;
        }

        if (!file_exists($img_font)) {
            return FALSE;
        }
        // OK - We're capable of doing this then...
        $img_height = $this->img_height;
        $img_width = $this->img_width;
        $img_font_size = $this->img_font_size;
        // Create  image
        $im = imagecreatetruecolor($img_width, $img_height);
        $background_color = imagecolorallocate($im, 0, 0, 0);
        $border_color = imagecolorallocate($im, 19, 65, 121);

        $transparent_color = imagecolortransparent($im, $background_color);
        $text_color = imagecolorexact($im, 19, 65, 121);

        $interior_width = $this->img_width - (2 * $this->margin);
        $interior_height = $this->img_height - (2 * $this->margin);
        $aText = $this->image_text();
        $line_height = floor($interior_height / count($aText));
        foreach ($aText as $index => $img_text) {
            $ypos = $this->margin + ($index * $line_height);
            imagestring($im, $this->img_font_size, $this->margin, $ypos, $img_text, $text_color);
        }
        imageantialias($im, TRUE);
        $img_name = 'password_quote.png';
        imagerectangle($im, 0, 0, $img_width - 1, $img_height - 1, $border_color);
        imagepng($im, $img_path . $img_name);
        $img = "<img src=\"$img_url$img_name\" width=\"$img_width\" height=\"$img_height\" style=\"border:0;\" alt=\"Password Quote\" />";
        imagedestroy($im);

        $this->img = $img;
    }

    /**
     * Splits the string into correct length strings so they fit correctly
     * into the image.
     * 
     * @return array Lines to be images
     */
    private function image_text() {
        $img_text = $this->img_text;
        $img_width = $this->img_width - (2 * $this->margin);
        $line = '';
        $text_lines = array();
        // Need to calculate the number of lines necessary to show the
        // text provided.
        // The integer 9 represents the pixel width of the GD built in mono-spaced font
        $font_width = imagefontwidth($this->img_font_size);
        $chars_per_line = floor($img_width / $font_width);
        $aString = explode(' ', $img_text);
        // split the string into the correct length lines
        while ($aString) {
            $line .= array_shift($aString) . ' ';
            if (count($aString) === 0 ||
                    strlen($line) + strlen($aString[0]) > $chars_per_line
            ) {
                $text_lines[] = $line;
                $line = '';
            }
        }
        // Return the array of lines to add
        return $text_lines;
    }

    /**
     * Select a random word and a random character from that word for use in
     * the string modification.  Also chooses a random character position in
     * which to inject the random character for validation.
     */
    private function randomize() {
        $img_text = $this->img_text;

        /**
         * Randomizer 
         */
        $aImg_text = explode(' ', $img_text);
        $rand_word_number = array_rand($aImg_text);
        $rand_char_number = rand(0, strlen($aImg_text[$rand_word_number]) - 1);
        $this->char = $aImg_text[$rand_word_number][$rand_char_number];
        // Translate from array indexes to human indexes (from 1) //
        $rand_char_number++;
        $rand_word_number++;
        // Random placement
        $this->placement = rand(1, $this->input_length);
        $randoms = array($rand_word_number, $rand_char_number, $this->placement);
        // Place appropriate suffixes for numerals
        foreach ($randoms as &$number) {
            $digit = abs($number) % 10;
            $ext = 'th';
            $ext = ((abs($number) % 100 < 21 && abs($number) % 100 > 4) ? 'th' /* mostly th */
                    : (($digit < 4) 
                        ? ($digit < 3) 
                        ? ($digit < 2) 
                        ? ($digit < 1) 
                        ? 'th' /* 0th */
                    : 'st' /* 1st, 21st... */
                    : 'nd' // 2nd, 22nd...
                    : 'rd' // 3rd, 23rd...
                    : 'th' //4th, 24th
                    )
            );
            $number = $number . $ext;
        }
        $this->rules = vsprintf($this->language->getText('unCaptcha_instructions'), array($randoms[1], $randoms[0], $randoms[2]));
    }

}

?>
